//////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) Tobias Zirr. All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "Range.h"

namespace D3DEffectsLite
{


typedef unsigned long long small_string_hash_type;

template <char C0 = 0, char C1 = 0, char C2 = 0, char C3 = 0, char C4 = 0, char C5 = 0, char C6 = 0, char C7 = 0>
struct SmallString
{
	static_assert(sizeof(small_string_hash_type) >= 8, "Small string hash type too small");
	static size_t const length = C7 ? 8 : C6 ? 7 : C5 ? 6 : C4 ? 5 : C3 ? 4 : C2 ? 3 : C1 ? 2 : C0 ? 1 : 0;
	static small_string_hash_type const hash =
		   (small_string_hash_type) C0
		+ ((small_string_hash_type) C1 << 8)
		+ ((small_string_hash_type) C2 << 8 * 2)
		+ ((small_string_hash_type) C3 << 8 * 3)
		+ ((small_string_hash_type) C4 << 8 * 4)
		+ ((small_string_hash_type) C5 << 8 * 5)
		+ ((small_string_hash_type) C6 << 8 * 6)
		+ ((small_string_hash_type) C7 << 8 * 7)
		;
};

// 0 for long strings!
template <class Range>
small_string_hash_type small_string_hash(Range const& chars, size_t count)
{
	small_string_hash_type hash = 0;
	if (count <= sizeof(hash))
		for (size_t i = 0; i < count; ++i)
			hash += (small_string_hash_type) chars[i] << 8 * i;
	return hash;
}

struct SmallStringHash
{
	small_string_hash_type value;

	SmallStringHash(char chr)
		: value((small_string_hash_type) chr) { }
	template <size_t Count>
	SmallStringHash(const char (&arr)[Count])
		: value(small_string_hash(arr, Count))
	{
		static_assert(Count <= sizeof(hash), "Too many characters for a small string");
	}
	template <char C0, char C1, char C2, char C3, char C4, char C5, char C6, char C7>
	SmallStringHash(SmallString<C0, C1, C2, C3, C4, C5, C6, C7> ss)
		: value(ss.hash) { }
	explicit SmallStringHash(unsigned long long hash)
		: value(hash) { }
};

struct HashedCharRange : CharRange
{
	small_string_hash_type hash;

	HashedCharRange()
		: hash() { }
	HashedCharRange(CharRange r)
		: CharRange(r)
		, hash(small_string_hash(r, size(r))) { }

	HashedCharRange& operator =(CharRange r)
	{
		*this = HashedCharRange(r);
		return *this;
	}
};

/// True, if the given character marks a line break.
bool is_endl(char c);

struct TokenType
{
	enum T
	{
		identifier,
		number,
		character,
		string,
		punct,
		endline,
		end
	};
};

struct Token
{
	TokenType::T type;
	HashedCharRange range;

	Token() { }
	Token(TokenType::T type, CharRange range)
		: type(type), range(range) { }
	Token(TokenType::T type, HashedCharRange range)
		: type(type), range(range) { }
};

inline bool operator ==(const Token &t1, const Token &t2)
{
	return t1.type == t2.type && t1.range == t2.range;
}
inline bool operator !=(const Token &t1, const Token &t2)
{
	return !(t1 == t2);
}

struct SmallToken
{
	TokenType::T type;
	small_string_hash_type hash;

	SmallToken(TokenType::T type, SmallStringHash hash)
		: type(type), hash(hash.value) { }
};

inline bool operator ==(const SmallToken &t1, const Token &t2)
{
	return t1.type == t2.type && t1.hash == t2.range.hash;
}
inline bool operator ==(const Token &t1, const SmallToken &t2)
{
	return t1.type == t2.type && t1.range.hash == t2.hash;
}
inline bool operator !=(const SmallToken &t1, const Token &t2)
{
	return !(t1 == t2);
}
inline bool operator !=(const Token &t1, const SmallToken &t2)
{
	return !(t1 == t2);
}

struct ParserFlags
{
	enum T
	{
		none = 0,
		keep_endl = 0x1,
		end_is_endl = 0x2 | keep_endl
	};
};

/// Gets the next token.
Token next_token(const char* &nextChar, const char *end, unsigned int flags = ParserFlags::none);

struct LexerCursor
{
	const char *nextChar;
	unsigned int line;

	LexerCursor(const char *nextChar, unsigned int line)
		: nextChar(nextChar),
		line(line) { }
};

struct LexerState
{
	const char *endChar;
	LexerCursor cursor;
	Token token;
	LexerCursor acceptedCursor;
	Token accepted;
	LexerCursor openCursor;
	unsigned int openFlags;
	bool isAccepted;

	LexerState(LexerCursor src, const char *srcEnd, unsigned int flags = ParserFlags::none)
		: endChar(srcEnd),
		cursor(src),
		acceptedCursor(src),
		isAccepted(true),
		openCursor(src),
		openFlags(flags) { }
};

/// Gets the next token.
Token& next_token(LexerState &state, unsigned int flags = ParserFlags::none);
/// Accepts the current token.
Token& accept_token(LexerState &state);
/// Reverts the current token.
Token& revert_token(LexerState &state);

struct AcceptOperator
{
	enum T { Identiy, Not };
};
inline bool apply(bool v, AcceptOperator::T op)
{
	return (op == AcceptOperator::Not) ? !v : v;
}

/// Checks for the given token type.
inline bool check_token(LexerState &state, TokenType::T type)
{
	return state.token.type == type;
}
/// Checks for the given token.
template <class Token>
inline bool check_token(LexerState &state, const Token &token)
{
	return state.token == token;
}

/// Checks for the given token type.
inline bool check_next_token(LexerState &state, TokenType::T type, unsigned int flags = ParserFlags::none)
{
	return next_token(state, flags).type == type;
}
/// Checks for the given token.
template <class Token>
inline bool check_next_token(LexerState &state, const Token &token, unsigned int flags = ParserFlags::none)
{
	return next_token(state, flags) == token;
}

/// Accepts the given token type.
inline bool accept_next_token(LexerState &state, TokenType::T type, unsigned int flags = ParserFlags::none, AcceptOperator::T op = AcceptOperator::Identiy)
{
	bool accepted = apply(check_next_token(state, type, flags), op);
	if (accepted)
		accept_token(state);
	return accepted;
}
/// Accepts the given token.
template <class Token>
inline bool accept_next_token(LexerState &state, const Token &token, unsigned int flags = ParserFlags::none, AcceptOperator::T op = AcceptOperator::Identiy)
{
	bool accepted = apply(check_next_token(state, token, flags), op);
	if (accepted)
		accept_token(state);
	return accepted;
}

/// Accepts the given token type.
inline bool dont_accept_next_token(LexerState &state, TokenType::T type, unsigned int flags = ParserFlags::none)
{
	return accept_next_token(state, type, flags, AcceptOperator::Not);
}
/// Accepts the given token.
template <class Token>
inline bool dont_accept_next_token(LexerState &state, const Token &token, unsigned int flags = ParserFlags::none)
{
	return accept_next_token(state, token, flags, AcceptOperator::Not);
}

} // namespace
